/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package evocarcassonne.core.elements;

import evocarcassonne.core.board.Tile;
import evocarcassonne.core.board.locations.Position;
import evocarcassonne.core.board.locations.Zone;
import evocarcassonne.core.game.Player;
import evocarcassonne.core.utils.Feature;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;

/**
 * @author pnaubourg
 */
public class Element {

    private Set<Tile> tiles = new HashSet();
    private HashMap<Pawn, Position> pawns = new LinkedHashMap();
    public List<OpenZone> openZones;

    private List<Feature> features = new ArrayList();


    private Element(Tile tile) {
        addTile(tile);
        openZones = new ArrayList();
    }

    public Element(Tile tile, List<Feature> feats) {
        this(tile);
        features = feats;
    }

    public final void addTile(Tile tile) {
        tiles.add(tile);
    }


    public boolean canAddPawn() {

        if (pawns.isEmpty()) {
            return true;
        } else {
            return false;
        }
    }

    public boolean isComplete() {
        return openZones.isEmpty();
    }

    public void addOpenZone(Tile tile, Zone zone) {

        OpenZone opZ = new OpenZone(tile, zone);
        openZones.add(opZ);

        for (Zone zone1 : zone.getLinks()) {
            OpenZone opZ1 = new OpenZone(tile, zone1);
            openZones.add(opZ1);
        }
    }

    public void removeOpenZone(Tile tile, Zone zone) {

        OpenZone oz = new OpenZone(tile, zone);
        openZones.remove(oz);

    }

    public void absorb(Element oldElt) {

        // merge pawns
        Set<Pawn> foreignPawns = oldElt.pawns.keySet();
        for (Pawn foreignPawn : foreignPawns) {
            pawns.put(foreignPawn, oldElt.pawns.get(foreignPawn));
        }

        //        System.out.println("merge openZones");


        //merge features
        features.addAll(oldElt.features);


        // merge openZones
        List<OpenZone> foreignOpenZones = oldElt.openZones;
        for (OpenZone foreignOpenZone : foreignOpenZones) {

//            if (!openZones.contains(foreignOpenZone)) {
            openZones.add(foreignOpenZone);
//            }

//            System.out.println(hashCode() + " absorb " + foreignOpenZone + " provenant de " + oldElt.hashCode());
        }

        //delete all OZ from oldElt
//        System.out.println("je nettoie "+oldElt.hashCode());
        oldElt.openZones.clear();


//        System.out.println(hashCode() + " posséde " + openZones.size() + " ouvertures : " + openZones);


        // merge tiles
        tiles.addAll(oldElt.tiles);

    }

    public HashMap<Pawn, Position> getPawns() {
        return pawns;
    }

    public Set<Tile> getTiles() {
        return tiles;
    }

    public void addPawn(Player player, Position pos) {

        if (player.pawns()) {
            pawns.put(player.getPawn(), pos);
        }
    }

    public boolean containsOZ(Tile nearTile, Zone nearZone) {

        OpenZone oz = new OpenZone(nearTile, nearZone);
        if (openZones.contains(oz)) {
            return true;
        } else {
            return false;
        }
    }

    public List<Feature> getFeatures() {
        return features;
    }

    public int evaluate() throws Exception {
        throw new Exception();
    }

}

class OpenZone {

    Tile tile;
    Zone zone;

    public OpenZone(Tile tile, Zone zone) {
        this.tile = tile;
        this.zone = zone;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final OpenZone other = (OpenZone) obj;

        if (tile != other.tile) {
            return false;
        }

        if (zone.card != other.zone.card) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 43 * hash + this.tile.hashCode();
        hash = 43 * hash + this.zone.hashCode();
        return hash;
    }

    @Override
    public String toString() {
        return "[ " + tile.toString() + " , " + zone.card + " ]";
    }
}
